package vip.xiaonuo.client.modular.testPaper.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.xiaonuo.auth.core.pojo.SaBaseClientLoginUser;
import vip.xiaonuo.auth.core.util.StpClientLoginUserUtil;
import vip.xiaonuo.auth.core.util.StpClientUtil;
import vip.xiaonuo.client.modular.examPaper.entity.ClientExamPaper;
import vip.xiaonuo.client.modular.examPaper.enums.ClientExamPaperSelectionModeEnum;
import vip.xiaonuo.client.modular.examPaper.service.ClientExamPaperService;
import vip.xiaonuo.client.modular.question.entity.ClientQuestion;
import vip.xiaonuo.client.modular.question.enums.QuestionDifficultyEnum;
import vip.xiaonuo.client.modular.question.enums.QuestionTypeEnum;
import vip.xiaonuo.client.modular.question.service.ClientQuestionService;
import vip.xiaonuo.client.modular.testPaper.entity.ClientTestPaper;
import vip.xiaonuo.client.modular.testPaper.dto.RandomSelectionConfigDTO;
import vip.xiaonuo.client.modular.testPaper.dto.TestPaperQuestionDTO;
import vip.xiaonuo.client.modular.testPaper.enums.ClientTestPaperStatusEnum;
import vip.xiaonuo.client.modular.testPaper.mapper.ClientTestPaperMapper;
import vip.xiaonuo.client.modular.testPaper.param.ClientTestPaperIdParam;
import vip.xiaonuo.client.modular.testPaper.param.SubmitTestPaperParam;
import vip.xiaonuo.client.modular.testPaper.result.TestPaperQuestionResult;
import vip.xiaonuo.client.modular.testPaper.result.TestPaperResult;
import vip.xiaonuo.client.modular.testPaper.service.ClientTestPaperService;
import vip.xiaonuo.common.cache.CommonCacheOperator;
import vip.xiaonuo.common.enums.CommonStatusEnum;
import vip.xiaonuo.common.enums.CommonYesNoEnum;
import vip.xiaonuo.common.exception.CommonException;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static vip.xiaonuo.common.consts.CacheConstant.TEST_PAPER_DAILY_ATTEMPT_LIMIT;

@Service
public class ClientTestPaperServiceImpl extends ServiceImpl<ClientTestPaperMapper, ClientTestPaper> implements ClientTestPaperService {

    @Resource
    private CommonCacheOperator commonCacheOperator;
    @Resource
    private ClientQuestionService clientQuestionService;
    @Resource
    private ClientExamPaperService clientExamPaperService;

    /**
     * 创建模拟试卷
     *
     * @param tblExamPaperId 试卷ID
     * @return 创建试卷
     */
    public ClientTestPaper createSimulateTestPaper(String tblExamPaperId) {
        String clientId = StpClientUtil.getLoginIdAsString();
        String now = DateUtil.now();
        QueryWrapper<ClientExamPaper> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ClientExamPaper::getId, tblExamPaperId)
                .eq(ClientExamPaper::getOnlyForExamHall, CommonYesNoEnum.NO.getCode())
                .le(ClientExamPaper::getStartDate, now)
                .ge(ClientExamPaper::getEndDate, now)
                .eq(ClientExamPaper::getStatus, CommonStatusEnum.ZC.getCode());
        ClientExamPaper tblExamPaper = clientExamPaperService.getOne(queryWrapper);
        if (Objects.isNull(tblExamPaper)) {
            throw new CommonException("试卷为不可用", tblExamPaperId);
        }
        String cacheKey = StrUtil.format(TEST_PAPER_DAILY_ATTEMPT_LIMIT, tblExamPaperId, clientId);
        Object cache = commonCacheOperator.get(cacheKey);
        int cacheUserLimit = 0;
        if (cache != null) {
            cacheUserLimit = NumberUtil.parseInt(cache.toString());
            if (cacheUserLimit >= tblExamPaper.getDailyAttemptLimit()) {
                throw new CommonException("今日考试次数已达上线", tblExamPaperId);
            }
        }
        ClientTestPaper tblTestPaper = this.createTestPaperBase(tblExamPaper, clientId);
        // 获取当前时间
        DateTime nowDate = DateUtil.date();
        // 获取今天的结束时间（即23:59:59）
        DateTime endOfDay = DateUtil.endOfDay(nowDate);
        commonCacheOperator.put(cacheKey, cacheUserLimit + 1, DateUtil.between(endOfDay, nowDate, DateUnit.SECOND, true));
        return tblTestPaper;
    }


    //==========================================创建试卷=======================================================================


    /**
     * 随机组卷
     *
     * @param randomSelectionConfig 组卷配置
     * @return 试卷试题
     */
    private ArrayList<TestPaperQuestionDTO> getRandomTestPaperQuestion(RandomSelectionConfigDTO randomSelectionConfig) {
        ArrayList<TestPaperQuestionDTO> testPaperQuestions = new ArrayList<>();
        randomSelectionConfig.getConfig().forEach(item -> {
            ArrayList<String> categoryIds = randomSelectionConfig.getCategoryIds();
            String type = item.getType();
            if (!item.getDifficulty()) {//不区分难度
                if (item.getNumber() > 0) {
                    ArrayList<TestPaperQuestionDTO> testPaperQuestionList = getTestPaperQuestionList(categoryIds, item.getNumber(), type, item.getScore(), null);
                    testPaperQuestions.addAll(testPaperQuestionList);
                }
            } else {//区分难度
                if (item.getDifficultySimpleNumber() > 0) {//简单
                    ArrayList<TestPaperQuestionDTO> testPaperQuestionList = getTestPaperQuestionList(categoryIds, item.getDifficultySimpleNumber(), type, item.getDifficultySimpleScore(), QuestionDifficultyEnum.SIMPLE);
                    testPaperQuestions.addAll(testPaperQuestionList);
                }
                if (item.getDifficultyGeneralNumber() > 0) {//中等
                    ArrayList<TestPaperQuestionDTO> testPaperQuestionList = getTestPaperQuestionList(categoryIds, item.getDifficultyGeneralNumber(), type, item.getDifficultyGeneralScore(), QuestionDifficultyEnum.SIMPLE);
                    testPaperQuestions.addAll(testPaperQuestionList);
                }
                if (item.getDifficultyHardNumber() > 0) {//困难
                    ArrayList<TestPaperQuestionDTO> testPaperQuestionList = getTestPaperQuestionList(categoryIds, item.getDifficultyHardNumber(), type, item.getDifficultyHardScore(), QuestionDifficultyEnum.SIMPLE);
                    testPaperQuestions.addAll(testPaperQuestionList);
                }
            }
        });
        return testPaperQuestions;
    }

    /**
     * 随机组题
     *
     * @param categoryIds   题库分类
     * @param number        数量
     * @param type          题目类型
     * @param questionScore 每题分数
     * @param difficulty    难度
     * @return 随机组题
     */
    private ArrayList<TestPaperQuestionDTO> getTestPaperQuestionList(ArrayList<String> categoryIds, Integer number, String type, BigDecimal questionScore, QuestionDifficultyEnum difficulty) {
        QueryWrapper<ClientQuestion> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ClientQuestion::getQuestionType, type)
                .last("ORDER BY rand()")
                .last(StrUtil.format("LIMIT {}", number));
        if (!categoryIds.isEmpty()) {
            queryWrapper.lambda().in(ClientQuestion::getCategoryId, categoryIds);
        }
        if (difficulty != null) {
            queryWrapper.lambda().eq(ClientQuestion::getDifficulty, difficulty.getValue());
        }
        List<ClientQuestion> questionList = clientQuestionService.list(queryWrapper);
        ArrayList<TestPaperQuestionDTO> tblTestPaperQuestions = new ArrayList<>();
        questionList.forEach(item -> {
            TestPaperQuestionDTO testPaperQuestion = new TestPaperQuestionDTO();
            testPaperQuestion.setId(item.getId());
            testPaperQuestion.setScore(questionScore);
            tblTestPaperQuestions.add(testPaperQuestion);
        });
        return tblTestPaperQuestions;
    }

    /**
     * 创建试卷基础
     *
     * @param tblExamPaper 试卷配置
     * @param clientId     客户ID
     * @return 试卷详情
     */
    private ClientTestPaper createTestPaperBase(ClientExamPaper tblExamPaper, String clientId) {
        UpdateWrapper<ClientExamPaper> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(ClientExamPaper::getId, tblExamPaper.getId())
                .setSql("`NUMBER`=`NUMBER`+1");
        clientExamPaperService.update(null, updateWrapper);
        //组题
        List<TestPaperQuestionDTO> testPaperQuestion = new ArrayList<>();
        if (Objects.equals(tblExamPaper.getSelectionMode(), ClientExamPaperSelectionModeEnum.RANDOM.getValue())) {
            //随机选题
            RandomSelectionConfigDTO randomSelectionConfig = JSONObject.parseObject(tblExamPaper.getSelectionConfig(), RandomSelectionConfigDTO.class);
            testPaperQuestion.addAll(getRandomTestPaperQuestion(randomSelectionConfig));
        } else {
            // 固定选题
            testPaperQuestion = JSONArray.parseArray(tblExamPaper.getSelectionConfig(), TestPaperQuestionDTO.class);
        }
        //创建试卷
        ClientTestPaper tblTestPaper = new ClientTestPaper();
        tblTestPaper.setPaperConfigId(tblExamPaper.getId());
        tblTestPaper.setClientId(clientId);
        tblTestPaper.setOrgId(tblExamPaper.getOrgId());
        tblTestPaper.setTotalScore(tblExamPaper.getTotalScore());
        tblTestPaper.setStatus(ClientTestPaperStatusEnum.UNDERWAY.getValue());
        tblTestPaper.setQuestion(JSONObject.toJSONString(testPaperQuestion));
        DateTime time = DateUtil.date();
        tblTestPaper.setStartTime(time);
        tblTestPaper.setEndTime(DateUtil.offsetMinute(time, tblExamPaper.getExamTimeLimit()));
        tblTestPaper.setQuestionNumber(testPaperQuestion.size());
        this.save(tblTestPaper);
        return tblTestPaper;
    }


    //=================================================================================================================
    private List<TestPaperQuestionResult> getTestPaperQuestionResults(String question, boolean isAnswer) {
        List<TestPaperQuestionDTO> testPaperQuestions = JSONArray.parseArray(question, TestPaperQuestionDTO.class);
        List<String> ids = testPaperQuestions.stream().map(TestPaperQuestionDTO::getId).toList();
        QueryWrapper<ClientQuestion> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(ClientQuestion::getId, ids);
        if (!isAnswer) {
            queryWrapper.lambda().select(ClientQuestion::getId, ClientQuestion::getQuestionTitle, ClientQuestion::getQuestionType, ClientQuestion::getIssueOption, ClientQuestion::getIssueVideo, ClientQuestion::getDifficulty);
        }
        List<ClientQuestion> clientQuestions = clientQuestionService.list(queryWrapper);
        Map<String, ClientQuestion> collect = clientQuestions.stream().collect(Collectors.toMap(ClientQuestion::getId, clientQuestion -> clientQuestion));
        List<TestPaperQuestionResult> list = new ArrayList<>();
        testPaperQuestions.forEach(item -> {
            TestPaperQuestionResult testPaperQuestionResult = new TestPaperQuestionResult();
            testPaperQuestionResult.setTestPaperQuestionDTO(item);
            testPaperQuestionResult.setQuestion(collect.get(item.getId()));
            list.add(testPaperQuestionResult);
        });
        return list;
    }

    @Override
    @Transactional
    public TestPaperResult getTestPaperInfo(ClientTestPaperIdParam clientTestPaperIdParam) {
        String clientId = StpClientUtil.getLoginIdAsString();
        QueryWrapper<ClientTestPaper> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ClientTestPaper::getId, clientTestPaperIdParam.getId())
                .eq(ClientTestPaper::getClientId, clientId)
                .eq(ClientTestPaper::getStatus, ClientTestPaperStatusEnum.UNDERWAY.getValue());
        ClientTestPaper clientTestPaper = this.getOne(queryWrapper);
        if (clientTestPaper == null) {
            throw new CommonException("试卷考试已结束");
        }
        TestPaperResult testPaperResult = new TestPaperResult();
        BeanUtil.copyProperties(clientTestPaper, testPaperResult);
        String question = clientTestPaper.getQuestion();
        List<TestPaperQuestionResult> testPaperQuestionResults = getTestPaperQuestionResults(question, false);
        testPaperResult.setQuestions(testPaperQuestionResults);
        return testPaperResult;
    }

    //=======================================提交试卷==========================================================================
    //填空题答案匹配
    private boolean fillAnswer(String questionAnswer, String userAnswer) {
        try {
            JSONArray questionAnswers = JSONArray.parseArray(questionAnswer);
            boolean isTrue = false;
            for (Object answer : questionAnswers) {
                for (Object o : (JSONArray) answer) {
                    if (StrUtil.equals((String) o, userAnswer)) {
                        isTrue = true;
                    }
                }
            }
            return isTrue;
        } catch (Exception e) {
            log.error("填空题答案解析失败", e);
            return false;
        }
    }

    //简答题答案匹配
    private double simpleAnswer(String questionAnswer, String userAnswer) {
        try {
            JSONObject question = JSONObject.parseObject(questionAnswer);
            String norm = question.getString("norm");
            if (StrUtil.equals(norm, userAnswer)) {
                return 1;
            }
            JSONArray keyword = question.getJSONArray("keyword");
            double scale = 0;
            for (Object o : keyword) {
                JSONObject item = (JSONObject) o;
                if (StrUtil.contains(userAnswer, item.getString("key"))) {
                    scale += item.getDouble("value");
                }
            }
            if (scale > 1) {
                return 1;
            }
            return scale;
        } catch (Exception e) {
            log.error("简答题答案解析失败", e);
            return 0;
        }
    }

    /**
     * 提交试卷
     *
     * @param submitTestPaperParam 提交试卷参数
     */
    @Override
    public ClientTestPaper submitTestPaper(SubmitTestPaperParam submitTestPaperParam) {
        //提交试卷
        String clientId = StpClientUtil.getLoginIdAsString();
        QueryWrapper<ClientTestPaper> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ClientTestPaper::getId, submitTestPaperParam.getId())
                .eq(ClientTestPaper::getClientId, clientId)
                .eq(ClientTestPaper::getStatus, ClientTestPaperStatusEnum.UNDERWAY.getValue());
        ClientTestPaper clientTestPaper = this.getOne(queryWrapper);
        if (clientTestPaper == null) {
            throw new CommonException("试卷考试已结束");
        }
        //用户答案
        Map<String, String> questions = submitTestPaperParam.getQuestions();
        //题目
        String question = clientTestPaper.getQuestion();
        //结算分数
        List<TestPaperQuestionDTO> testPaperQuestions = JSONArray.parseArray(question, TestPaperQuestionDTO.class);
        List<String> ids = testPaperQuestions.stream().map(TestPaperQuestionDTO::getId).toList();
        QueryWrapper<ClientQuestion> questionQueryWrapper = new QueryWrapper<>();
        questionQueryWrapper.lambda().in(ClientQuestion::getId, ids);
        List<ClientQuestion> clientQuestions = clientQuestionService.list(questionQueryWrapper);
        Map<String, ClientQuestion> collect = clientQuestions.stream().collect(Collectors.toMap(ClientQuestion::getId, clientQuestion -> clientQuestion));
        AtomicReference<BigDecimal> clientScoreHolder = new AtomicReference<>(BigDecimal.ZERO);
        AtomicInteger correctQuestionNumber = new AtomicInteger();
        testPaperQuestions.forEach(item -> {
            ClientQuestion clientQuestion = collect.get(item.getId());
            String userAnswer = questions.get(item.getId());
            item.setUserAnswer(userAnswer);
            BigDecimal userScore = BigDecimal.valueOf(0);
            if (clientQuestion == null) {
                log.error(StrUtil.format("试题不存在：{}", item.getId()));
                userScore = BigDecimal.valueOf(0);
            } else if (clientQuestion.getQuestionType().equals(QuestionTypeEnum.FILL.getValue())) {
                //处理填空题
                if (fillAnswer(clientQuestion.getAnswer(), userAnswer)) {
                    userScore = item.getScore();
                }
            } else if (clientQuestion.getQuestionType().equals(QuestionTypeEnum.SIMPLE.getValue())) {
                //处理简答题
                double scale = simpleAnswer(clientQuestion.getAnswer(), userAnswer);
                userScore = BigDecimal.valueOf(item.getScore().doubleValue() * scale);
            } else if (StrUtil.equals(userAnswer, clientQuestion.getAnswer())) {
                //处理其他
                userScore = item.getScore();
            }
            clientScoreHolder.set(clientScoreHolder.get().add(userScore));
            item.setUserScore(userScore);
            if (Objects.equals(userScore, item.getScore())) {
                correctQuestionNumber.getAndIncrement();
            }
        });
        //更新试卷
        clientTestPaper.setClientScore(clientScoreHolder.get());
        clientTestPaper.setStatus(ClientTestPaperStatusEnum.END.getValue());
        clientTestPaper.setActualEndTime(DateUtil.date());
        clientTestPaper.setQuestion(JSONArray.toJSONString(testPaperQuestions));
        long examDuration = DateUtil.between(clientTestPaper.getStartTime(), clientTestPaper.getActualEndTime(), DateUnit.MINUTE, false);
        clientTestPaper.setExamDuration((int) examDuration);
        clientTestPaper.setCorrectQuestionNumber(correctQuestionNumber.get());
        this.updateById(clientTestPaper);
        return clientTestPaper;
    }


    //=================================================================================================================

    @Override
    public TestPaperResult getTestPaperAnswerInfo(ClientTestPaperIdParam clientTestPaperIdParam) {
        QueryWrapper<ClientTestPaper> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ClientTestPaper::getId, clientTestPaperIdParam.getId())
                .eq(ClientTestPaper::getStatus, ClientTestPaperStatusEnum.UNDERWAY.getValue());
        ClientTestPaper clientTestPaper = this.getOne(queryWrapper);
        if (clientTestPaper == null) {
            throw new CommonException("试卷考试已结束");
        }
        TestPaperResult testPaperResult = new TestPaperResult();
        BeanUtil.copyProperties(clientTestPaper, testPaperResult);
        String question = clientTestPaper.getQuestion();
        List<TestPaperQuestionResult> testPaperQuestionResults = getTestPaperQuestionResults(question, true);
        testPaperResult.setQuestions(testPaperQuestionResults);
        return testPaperResult;
    }

}
